/* IncomingMessage__4_7.java
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 * 
 */
package org.jimm.protocols.icq.packet.received.icbm;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.util.List;

import android.util.Log;
import org.jimm.protocols.icq.core.OscarConnection;
import org.jimm.protocols.icq.exceptions.BadPacketException;
import org.jimm.protocols.icq.exceptions.ConvertStringException;
import org.jimm.protocols.icq.integration.events.IncomingMessageEvent;
import org.jimm.protocols.icq.integration.events.IncomingUrlEvent;
import org.jimm.protocols.icq.integration.events.XStatusRequestEvent;
import org.jimm.protocols.icq.integration.listeners.MessagingListener;
import org.jimm.protocols.icq.integration.listeners.XStatusListener;
import org.jimm.protocols.icq.packet.received.ReceivedPacket;
import org.jimm.protocols.icq.setting.enumerations.MessageFlagsEnum;
import org.jimm.protocols.icq.setting.enumerations.MessageTypeEnum;
import org.jimm.protocols.icq.setting.enumerations.StatusModeEnum;
import org.jimm.protocols.icq.tool.StringTools;
import org.jimm.protocols.icq.tool.Util;

/**
 * <p>
 * Created by
 * 
 * @author Алексей Коновалов
 * @author Fabrice Michellonet
 */
public class IncomingMessage__4_7 extends ReceivedPacket {

	private static final String LOG_TAG = "ICQ:IncomingMessage__4_7";

	public static final int UCS2BE_ENCODING_MASK = 0x00020000;

	// Типы Tlv
	private static final int TLV_TYPE_USER_STATUS = 0x06;

	// Типы форматов сообщений
	private static final int CHANNEL_1_MESSAGE_FORMAT = 0x0001;
	private static final int CHANNEL_2_MESSAGE_FORMAT = 0x0002;
	private static final int CHANNEL_4_MESSAGE_FORMAT = 0x0004;

	private int time;
	private int msgId;
	private int messageChannel;
	// private Tlv userIdleTime;
	// private RawData ackType;
	// private RawData senderTcpVer;
	// private RawData msgSeqNum;
	private int msgType;
	private byte msgFlags;
	// private RawData foreground;
	// private RawData background;
	private String senderID;
	private String message;
	private String url;
	private Integer senderStatus;
	// private Tlv onlineSinceTime;
	// private Tlv ackType2;
	// private Tlv port;
	// private Tlv ip;

	// private boolean isRequestAwayMessage = false;
	//private boolean isRequestXStatus = false;

	// private boolean isFileAckOrAbortRequest = false;
	// private boolean isFileAckOrFileOk = false;

	// TODO must normal recive RTF messages!
	public IncomingMessage__4_7(DataInputStream strm1)
			throws ConvertStringException, IOException, BadPacketException {
		super(strm1, true);

		// FIXME временно
		DataInputStream strm = new DataInputStream(new ByteArrayInputStream(
				getSnac().getDataFieldByteArray()));

		/* retreiving TIME */
		time = strm.readInt();

		/* retreiving ID */
		msgId = strm.readInt();

		/* retreiving message channel */
		messageChannel = strm.readUnsignedShort();

		/* retreiving sender's ID */
		byte idLen = strm.readByte();
		senderID = StringTools.utf8ByteArrayToString(strm, idLen);

		/* skipping sender warning level */
		strm.skip(2);

		/* Retreive the number of TLV */
		int nbTlv = strm.readUnsignedShort();

		for (int i = 0; i < nbTlv; ++i) {
			int tlvType = strm.readUnsignedShort();
			int tlvLen = strm.readUnsignedShort();

			switch (tlvType) {
			case TLV_TYPE_USER_STATUS:
				senderStatus = strm.readInt();
				if (tlvLen != 4)
					throw new BadPacketException();
				break;
			// case 0x0F:
			// // userIdleTime = tmpTlv;
			// break;
			// case 0x03:
			// // onlineSinceTime = tmpTlv;
			// break;
			default:
				strm.skip(tlvLen);
			}
		}

		switch (messageChannel) {
		case CHANNEL_1_MESSAGE_FORMAT:
			parseType1(strm);
			break;
		case CHANNEL_2_MESSAGE_FORMAT:
			parseType2(strm);
			break;
		case CHANNEL_4_MESSAGE_FORMAT:
			parseType4(strm);
			break;
		default:
			throw new BadPacketException();
		}

//		if (getMessageType().getType() == MessageTypeEnum.XSTATUS_MESSAGE) {
//			isRequestXStatus = true;
//		}
	}

	public void execute(OscarConnection connection) throws Exception {
		/*
		 * send the acknowledge if not type 4 and MessageType not type
		 * XSTATUS_REQUEST
		 */
		// if (messageChannel == 2 && !isRequestXStatus) {
		// connection.sendFlap(new MessageType2Ack(time, msgId, senderID,
		// senderTcpVer, msgSeqNum, msgType, msgFlags, connection
		// .getTweaker().getRequestMessage(msgType)));
		// }
	}

	public void notifyEvent(OscarConnection connection) {
		if (getMessageType().getType() == MessageTypeEnum.PLAIN_MESSAGE)
			notifyIncomingMessage(connection);
		else if (getMessageType().getType() == MessageTypeEnum.URL)
			notifyIncomingUrl(connection);
		else if (getMessageType().getType() == MessageTypeEnum.XSTATUS_MESSAGE)
			notifyXStatusRequest(connection);
		else {
			Log.e(LOG_TAG, "UNRECOGNIZED IncomingMessage__4_7 TYPE: "
					+ getMessageType().getType() + " (norm "
					+ MessageTypeEnum.PLAIN_MESSAGE + ", url "
					+ MessageTypeEnum.URL + "," + " xstatusreq "
					+ MessageTypeEnum.XSTATUS_MESSAGE + ")");
		}
	}

	private void notifyIncomingMessage(OscarConnection connection) {
		final IncomingMessageEvent e = new IncomingMessageEvent(this);
		List<MessagingListener> listeners = connection.getMessagingListeners();
		for (MessagingListener l : listeners) {
			Log.d(LOG_TAG, "notify listener " + l.getClass().getName()
					+ " onIncomingMessage()");
			l.onIncomingMessage(e);
		}
	}

	private void notifyXStatusRequest(OscarConnection connection) {
		final XStatusRequestEvent e = new XStatusRequestEvent(this);
		List<XStatusListener> listeners = connection.getXStatusListeners();
		for (XStatusListener l : listeners) {
			Log.d(LOG_TAG, "notify listener " + l.getClass().getName()
					+ " onXStatusRequest()");
			l.onXStatusRequest(e);
		}
	}

	private void notifyIncomingUrl(OscarConnection connection) {
		final IncomingUrlEvent e = new IncomingUrlEvent(this);
		List<MessagingListener> listeners = connection.getMessagingListeners();
		for (MessagingListener l : listeners) {
			Log.d(LOG_TAG, "notify listener " + l.getClass().getName()
					+ " onIncomingUrl()");
			l.onIncomingUrl(e);
		}
	}

	/**
	 * This method parse Type 1 message, which is the oldest way to send a
	 * message.
	 * 
	 * @param position
	 *            The position where we must start to parse.
	 * @param data
	 *            The packet we must parse.
	 * @throws ConvertStringException
	 * @throws IOException
	 * @throws BadPacketException
	 */
	private void parseType1(DataInputStream strm)
			throws ConvertStringException, IOException, BadPacketException {
		/* Setting up the msgType property to NORMAL MESSAGE */
		msgType = 0x01;

		int tlvType = strm.readUnsignedShort();
		if (tlvType != 2)
			throw new BadPacketException();
		strm.skip(2); // tlvLen

		// capability TLV
		tlvType = strm.readUnsignedShort();
		if (tlvType != 0x501)
			throw new BadPacketException();
		int tlvLen = strm.readUnsignedShort();

		strm.skip(tlvLen);

		// message TLV
		tlvType = strm.readUnsignedShort();
		if (tlvType != 0x0101)
			throw new BadPacketException();
		tlvLen = strm.readUnsignedShort();

		int encoding = strm.readInt();
		/* retreiving the message itself */
		if ((encoding & UCS2BE_ENCODING_MASK) != 0)
			message = StringTools.ucs2beByteArrayToString(strm, tlvLen - 4);
		else
			message = StringTools.byteArrayToString(strm, tlvLen - 4);
	}

	private void parseType2(DataInputStream strm)
			throws ConvertStringException, IOException, BadPacketException {

		int tlvType = strm.readUnsignedShort();
		if (tlvType != 0x05)
			throw new BadPacketException();

		strm.skip(2); // tlv length

		int cmd = strm.readUnsignedShort();
		if (cmd != 0)
			return; // normal message

		strm.skip(4 + 4 + 16);

		for (;;) {
			try {
				tlvType = strm.readShort();
			} catch (EOFException e) {
				break;
			}
			int tlvLen = strm.readShort();
			// тут можно прочитать ip, port и т.д
			if (tlvType == 0x2711) {
				int folowingLength = Util.readShortLittleEndian(strm);
				strm.skip(folowingLength + 2 + 2 + 12);

				msgType = strm.readByte();
				msgFlags = strm.readByte();

				if (msgType == 0x01) {
					parseType2Message(strm);
					break;
				}
			} else
				strm.skip(tlvLen);
		}
	}

	private void parseType2Message(DataInputStream strm)
			throws ConvertStringException, IOException {
		strm.skip(4); // status and priority

		/* Retreiving message length */
		int msgLen = Util.readShortLittleEndian(strm);

		/* Retreiving message */
		message = StringTools.utf8ByteArrayToString(strm, msgLen);

		/* Retreiving foreground info */
		// foreground = new RawData(data, position, 4);
		strm.skip(4);

		/* Retreiving background info */
		// background = new RawData(data, position, 4);
		strm.skip(4);
	}

	private void parseType4(DataInputStream strm)
			throws ConvertStringException, IOException {

		/* skipping the first 4 bytes of the TLV(5) */
		strm.skip(4);

		/* skipping sender's id field */
		strm.skip(4);

		/* retreiving message type field */
		msgType = strm.readUnsignedShort();

		/* Retreiving message length */
		int msgLen = strm.readUnsignedShort();

		if (msgType == 0x01 || msgType == 0x04)
			message = StringTools.utf8ByteArrayToString(strm, msgLen);
	}

	public int getMessageId() {
		return msgId;
	}

	public int getMessageChannel() {
		return messageChannel;
	}

	public String getSenderID() {
		return senderID;
	}

	// public int getSenderTcpVersion() {
	// return senderTcpVer.getValue();
	// }

	// public int getMessageSeqNum() {
	// return msgSeqNum.getValue();
	// }

	public String getMessage() {
		return message;
	}

	public String getUrl() {
		return url;
	}

	public StatusModeEnum getSenderStatus() {
		/* AIM do not provide status */
		if (senderStatus == null)
			return new StatusModeEnum(StatusModeEnum.ONLINE);
		return new StatusModeEnum(senderStatus);
	}

	public MessageFlagsEnum getMessageFlag() {
		return new MessageFlagsEnum(msgFlags);
	}

	public MessageTypeEnum getMessageType() {
		return new MessageTypeEnum(msgType);
	}

	public int getTime() {
		return time;
	}

	// private boolean isRequestAwayMessage() {
	// return isRequestAwayMessage;
	// }
	//
	// private boolean isFileAckOrAbortRequest() {
	// return isFileAckOrAbortRequest;
	// }
	//
	// private boolean isRequestXStatus() {
	// return isRequestXStatus;
	// }
	//
	// private boolean isFileAckOrFileOk() {
	// return isFileAckOrFileOk;
	// }
}
